package com.webull.information.center.dao;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.webull.framework.mvc.ApiException;
import com.webull.framework.util.UtilJson;

/**
 * 新闻中心数据库分表工具类
 * 
 * @Description
 * @author smj
 * @Date 上午11:43:19
 * @version jdk1.8
 */

@Component
public class DbShardHelper {
	protected final Logger logger = LogManager.getLogger(getClass());

	private List<String> tables = new ArrayList<>();
	private List<Tag> tablesTag = new ArrayList<>();
	private @Autowired JdbcTemplate jdbcTemplate;

	/**
	 * <pre>
	 *  -- 提前建表需替换后缀
	    CREATE table wl_information.wli_news_2 like wl_information.wli_news;
		CREATE table wl_information.wli_news_future_cat_rel_2  like wl_information.wli_news_future_cat_rel;
		CREATE table wl_information.wli_news_ticker_rel_2  like wl_information.wli_news_ticker_rel;
	 * </pre>
	 */
	{
		tables.add("wli_news");
		tables.add("wli_news_ticker_rel");
		tables.add("wli_news_future_cat_rel");
		//
		//
		//
		tablesTag.add(Tag.produce(-99l, 170 * wan, ""));// 兼容部分lastid为-1的情况
		tablesTag.add(Tag.produce(170 * wan, 500 * wan, "_2"));
		tablesTag.add(Tag.produce(500 * wan, 900 * wan, "_3"));
		tablesTag.add(Tag.produce(900 * wan, 1300 * wan, "_4"));
		Collections.sort(tablesTag, // 确保正序排列
				(c1, c2) -> ObjectUtils.compare(c1.getBegin(), c2.getBegin(), false));
	}
	private static long wan = 10000l;
	public String shardDealSql(String sql, String suff) {
		sql = replaceBySuffix(sql, suff);
		logger.info("DbShardHelper shardDealSql newsId:{} sql:{}", suff, sql);
		return sql;
	}

	public String shardDealSql(String sql, Long newsId) {
		String suff = getSuffixByNewsID(newsId);
		sql = replaceBySuffix(sql, suff);
		logger.info("DbShardHelper shardDealSql newsId:{} sql:{}", newsId, sql);
		return sql;
	}

	/**
	 * 只查最新的作用表
	 * 
	 * <pre>
	 * 降低使用标准
	 * </pre>
	 */
	public String shardReleaseSql(String sql) {
		String suff = getSuffixByNewsID(currNewsId());
		sql = replaceBySuffix(sql, suff);
		logger.info("DbShardHelper shardReleaseSql sql:{}", sql);
		return sql;
	}

	/**
	 * 最新的Suffix
	 * 
	 * @return
	 */
	public String currentSuffix() {
		return getSuffixByNewsID(currNewsId());
	}

	/*
	 * 所有的新闻表名集合
	 */
	private AllNewsTable allNewsSuffix;

	public List<String> allNewsSuffix() {
		String suff = getSuffixByNewsID(currNewsId());

		if (allNewsSuffix != null && StringUtils.equals(suff, allNewsSuffix.getCurrSuffix()))
			return allNewsSuffix.getAllNewsTable();
		/*
		 * 
		 */
		sync_: synchronized (this) {
			if (allNewsSuffix != null && StringUtils.equals(suff, allNewsSuffix.getCurrSuffix()))
				break sync_;
			/*
			 * 
			 */
			AllNewsTable tables = new AllNewsTable();
			tables.setCurrSuffix(suff);
			tables.getAllNewsTable().add(suff);
			for_: for (int i = 0; i < tablesTag.size(); i++) {
				if (StringUtils.equals(suff, tablesTag.get(i).suffix))
					break for_;
				tables.getAllNewsTable().add(tablesTag.get(i).suffix);
			}

			allNewsSuffix = tables;
			logger.info("DbShardHelper allNewsTable list:{}", UtilJson.toString(allNewsSuffix));
		}
		/*
		 * 
		 */
		return allNewsSuffix.getAllNewsTable();
	}

	private String replaceBySuffix(String sql, String suff) {
		if (suff == null)
			throw new ApiException("replaceSuffix is null sql:" + sql);
		if (suff.length() == 0)
			return sql;
		for (int i = 0; i < tables.size(); i++) {
			/*
			 * 忽略大小写
			 */
			sql = StringUtils.replacePattern(sql, "(?i)" + tables.get(i) + "\\s{1,1}", tables.get(i) + suff + " ")
					.intern();
		}
		return sql;
	}

	private String getSuffixByNewsID(Long newsId) {
		for (int i = 0; i < tablesTag.size(); i++) {
			if (tablesTag.get(i).belongRange(newsId))
				return tablesTag.get(i).getSuffix();
		}
		return null;
	}

	/**
	 * newsid所在的shard的下一个shared的suffix
	 * 
	 * @param newsId
	 * @return
	 */
	public String nextSuffixByNewsId(Long newsId) {
		for (int i = 0; i < tablesTag.size(); i++) {
			if (tablesTag.get(i).belongRange(newsId) && (i + 1) < tablesTag.size())
				return tablesTag.get(i + 1).getSuffix();
		}
		return null;
	}

	/**
	 * 当前shard的前一个shared的suffix
	 * 
	 * @return
	 */
	public String prevSuffixByCurrent() {
		Long newsId = currNewsId();
		for (int i = 0; i < tablesTag.size(); i++) {
			if (tablesTag.get(i).belongRange(newsId) && (i - 1) >= 0)
				return tablesTag.get(i - 1).getSuffix();
		}
		return null;
	}

	/*
	 * 获取当前新闻最大id值
	 */
	private Long currNewsId() {
		return jdbcTemplate.queryForObject("select current_val from wli_sequence where seq_name = 'wli_news_id'",
				Long.class);
	}

	/*
	 * 获取当前新闻最大id值
	 */
	@Transactional("transactionManager")
	public Long nextNewsId() {
		jdbcTemplate.update(
				"update wli_sequence set current_val = current_val + increment_val where seq_name = 'wli_news_id'");
		return currNewsId();
	}

	/**
	 * 新闻id区间对象
	 * 
	 * @Description
	 * @author smj
	 * @Date 下午2:28:35
	 * @version jdk1.8
	 */
	private static class Tag {
		private Long begin;
		private Long end;
		private String suffix;

		private Long getBegin() {
			return begin;
		}

		private String getSuffix() {
			return suffix;
		}

		private boolean belongRange(Long idVal) {
			return (end == null || idVal <= end)//
					&& (begin == null || idVal > begin);
		}

		public static Tag produce(Long begin, Long end, String suffix) {
			Tag t = new Tag();
			t.begin = begin;
			t.end = end;
			t.suffix = suffix;
			return t;
		}

	}

	private static class AllNewsTable {
		private String currSuffix;// 最新的suffix,判断是否需要刷新allNewsTable
		private List<String> allNewsTable = new ArrayList<>();

		public String getCurrSuffix() {
			return currSuffix;
		}

		public void setCurrSuffix(String currSuffix) {
			this.currSuffix = currSuffix;
		}

		public List<String> getAllNewsTable() {
			return allNewsTable;
		}

	}

}
